use std::collections::HashMap;

use apikit::rpc::{ParamAppender, RequestParamSign};
use reqwest::RequestBuilder;
use serde::Serialize;
use thiserror::Error;
use uri_template_ex::UriTemplate;

use crate::param::{__TypeMapper, Path};

impl<T> RequestParamSign<Path<T>> for __TypeMapper {
    type Input = Path<T>;
}

#[derive(Debug, Error)]
pub enum Error {
    #[error(transparent)]
    ReqwestError(#[from] reqwest::Error),

    #[error(transparent)]
    UriError(#[from] uri_template_ex::Error),

    #[error(transparent)]
    SerdeError(#[from] serde_json::Error),
}

impl<T: Serialize> ParamAppender<RequestBuilder> for Path<T> {
    type Error = Error;

    fn append_param(self, builder: RequestBuilder) -> Result<RequestBuilder, Self::Error> {
        fn to_uri_vars<T: Serialize>(
            value: &T,
        ) -> Result<HashMap<String, String>, serde_json::Error> {
            let value = serde_json::to_value(value)?;
            let mut map = HashMap::new();
            if let serde_json::Value::Object(obj) = value {
                for (k, v) in obj {
                    let s = v.as_str().unwrap_or(&v.to_string()).to_string();
                    map.insert(k, s);
                }
            }
            Ok(map)
        }

        let (client, request) = builder.build_split();
        let mut request = request?;
        let url = request.url_mut().as_str();
        let template = UriTemplate::new(url)?;
        let vars: HashMap<String, String> = to_uri_vars(&self.0)?;
        let url = template.expand(&vars);
        request.url_mut().set_path(url.as_str());
        let builder = RequestBuilder::from_parts(client, request);
        Ok(builder)
    }
}
